package com.neo.push.client;

import com.neo.push.client.handler.ClientHearBeatTrigger;
import com.neo.push.client.handler.ClientMessageHandler;
import com.neo.push.client.handler.PongDiscardHandler;
import com.neo.push.client.listner.PushClientListener;
import com.neo.push.core.RegisterMessage;
import com.neo.push.core.SocketRegisterMessage;
import com.neo.push.core.codec.MessageDecoderHandler;
import com.neo.push.core.codec.MessageEncoderHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.util.concurrent.TimeUnit;

/**
 * Created by lock on 17-8-2.
 */
public class BasePushClient implements PushClient {


    private final String host;
    private final int port;
    private EventLoopGroup workGroup;
    private volatile boolean running = true;
    private ChannelFuture channelFuture;
    private int times;
    private int maxCycle;
    private int hearBeatSeconds;
    private PushClientListener listener;



    /**
     *
     * @param host              连接的推送服务器地址
     * @param port              连接的端口
     * @param cycle             自动重连的次数
     * @param listener          监听器
     */
    public BasePushClient(String host, int port, int cycle,PushClientListener listener) {
        this(host,port,cycle,listener, (int) TimeUnit.MINUTES.toSeconds(3));
    }


    /**
     *
     * @param host              连接的推送服务器地址
     * @param port              连接的端口
     * @param cycle             自动重连的次数
     * @param listener          监听器
     * @param hearBeatSeconds   心跳间隔时间
     */
    public BasePushClient(String host, int port, int cycle,PushClientListener listener,int hearBeatSeconds) {
        this.host = host;
        this.port = port;
        this.listener = listener;
        this.maxCycle = cycle;
        this.hearBeatSeconds = hearBeatSeconds;
    }





    private void reConnectService() throws Exception {
        if(times<maxCycle&&isRunning()){
            TimeUnit.SECONDS.sleep(5);
            times++;
            connectServer();
        }else{
            //有可能是 times<maxCycle 也有可能时 running = false,在这里重新设置
            this.running = false;
            listener.closed(this);
        }
    }


    private void startConnect() throws Exception {
        if(!running){
            return;
        }
        workGroup = new NioEventLoopGroup(2);
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(workGroup).channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE,true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline()
                                .addLast(new MessageDecoderHandler())
                                .addLast(new MessageEncoderHandler())
                                .addLast(new ClientHearBeatTrigger(hearBeatSeconds))
                                .addLast(new PongDiscardHandler())
                                .addLast(new ClientMessageHandler(listener));
                    }
                });
        channelFuture = bootstrap.connect(host,port).sync();
        times = 0;
        listener.connected(this);
        channelFuture.channel().closeFuture().sync();
    }

    @Override
    public void connectServer() throws Exception {
        running = true;
        try{
            startConnect();
        }catch (Exception ex){
            ex.printStackTrace();
            throw ex;
        }finally {
            if(null!=workGroup){
                workGroup.shutdownGracefully();
            }
            reConnectService();
        }
    }

    @Override
    public void stopClient() throws Exception {
        running = false;
        channelFuture.channel().close();
    }

    @Override
    public void registerConnection(RegisterMessage message) throws Exception {
        channelFuture.channel().writeAndFlush(new SocketRegisterMessage(message));
    }


    @Override
    public boolean isRunning() {
        return this.running;
    }
}
